package biz.yfsoft.app.fastframework.plugin.mongodb;

import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Pattern;

import com.jfinal.log.Logger;
import com.jfinal.plugin.activerecord.Page;
import com.jfinal.plugin.activerecord.Record;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;

public class MongoKit {

    protected static Logger logger = Logger.getLogger(MongoKit.class);

    private static MongoClient client;
    private static DB defaultDb;

    public static void init(MongoClient client, String database) {
        MongoKit.client = client;
        MongoKit.defaultDb = client.getDB(database);

    }

    /**
     * 更新
     * @param collectionName
     * @param q 更新的条件
     * @param o 要更新的值
     */
    public static void update(String collectionName, Map<String, Object> q, Map<String, Object> o){
    	DBObject query = new BasicDBObject();
    	query.putAll(q);
    	DBObject set = new BasicDBObject();
    	set.putAll(o);
    	BasicDBObject doc = new BasicDBObject();  
        doc.put("$set", set);
    	MongoKit.getCollection(collectionName).update(query, doc, false, true);
    }
    
    /**
     * 查询总数
     * @param collectionName 表
     * @param conditionMap	 查询条件
     * @return
     */
    public static long count(String collectionName, Map<String, Object> conditionMap){
    	DBObject conditions = new BasicDBObject();
    	if(conditionMap.size() > 0){
    		for (Iterator<String> iterator = conditionMap.keySet().iterator(); iterator.hasNext();) {
				String key = iterator.next();
				conditions.put(key, conditionMap.get(key));
			}
    	}
    	return MongoKit.getCollection(collectionName).count(conditions);
    }
    
    /**
     * 分组
     * @param collectionName 表
     * @param keys 			 分组的字段
     * @param conditionMap	 查询条件
     * @return
     */
    public static BasicDBList findGroud(String collectionName, String[] keys, Map<String, Object> conditionMap){
    	DBObject dbkeys = new BasicDBObject();
    	if(keys.length >0){
    		for (String key : keys)
    			dbkeys.put(key, true);
    	}
    	
    	DBObject conditions = new BasicDBObject();
    	if(conditionMap.size() > 0){
    		for (Iterator<String> iterator = conditionMap.keySet().iterator(); iterator.hasNext();) {
				String key = iterator.next();
				conditions.put(key, conditionMap.get(key));
			}
    	}
    	
    	DBObject initial = new BasicDBObject(); 
        DBObject index = new BasicDBObject(); 
        index.put("count", 0); 
        index.put("list", new ArrayList<Object>()); 
        initial.put("info", index); 
        
        String reduce = "function (doc, out) { "
                + " out.info.count = out.info.count+=1; "
                + " out.info.list.push(doc);"
            + "}"; 
    	
        return (BasicDBList) MongoKit.getCollection(collectionName).group(dbkeys, conditions, initial, reduce);
    }
    
    public static void main(String[] args) {
		try {
			MongoClient client = new MongoClient("localhost", 27017);
			MongoKit.init(client, "app");
			DBObject dbo = new BasicDBObject();
			dbo.put("path", "file:/Users/yanhao");
			MongoKit.getCollection("files").save(dbo);
			System.out.println(dbo);
			
//			Map<String, Object> conditionMap = new HashMap<String, Object>();
//			conditionMap.put("buyer", 1026);
//			BasicDBList result = MongoKit.findGroud("cart", new String[]{"userid"}, conditionMap);
//			System.out.println(result);
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
    
    
    public static void updateFirst(String collectionName, Map<String, Object> q, Map<String, Object> o) {
        MongoKit.getCollection(collectionName).findAndModify(toDBObject(q), toDBObject(o));
    }

    public static int removeAll(String collectionName) {
        return MongoKit.getCollection(collectionName).remove(new BasicDBObject()).getN();
    }

    public static int remove(String collectionName, Map<String, Object> filter) {
        return MongoKit.getCollection(collectionName).remove(toDBObject(filter)).getN();
    }

    public static int save(String collectionName, List<Record> records) {
        List<DBObject> objs = new ArrayList<DBObject>();
        for (Record record : records) {
            objs.add(toDbObject(record));
        }
        return MongoKit.getCollection(collectionName).insert(objs).getN();

    }

    @SuppressWarnings("unchecked")
	public static int save(String collectionName, Record record) {
    	BasicDBObject object = new BasicDBObject();
        object.putAll(record.getColumns());
        int n = MongoKit.getCollection(collectionName).save(object).getN();
        record.setColumns(object.toMap());
        return n;
    }

    public static Record findFirst(String collectionName) {
        return toRecord(MongoKit.getCollection(collectionName).findOne());
    }

    public static Record findOne(String collectionName, Record record){
    	return toRecord(MongoKit.getCollection(collectionName).findOne(toDbObject(record)));
    	
    }
    
    public static Page<Record> paginate(String collection, int pageNumber, int pageSize) {
        return paginate(collection, pageNumber, pageSize, null, null, new BoSorter());
    }

    public static Page<Record> paginate(String collection, int pageNumber, int pageSize, Map<String, Object> filter) {
        return paginate(collection, pageNumber, pageSize, filter, null,  new BoSorter());
    }

    public static Page<Record> paginate(String collection, int pageNumber, int pageSize, Map<String, Object> filter,
            Map<String, Object> like) {
        return paginate(collection, pageNumber, pageSize, filter, like, new BoSorter());
    }

    public static Page<Record> paginate(String collection, int pageNumber, int pageSize, Map<String, Object> filter,
            Map<String, Object> like, Map<String, Object> sort) {
        DBCollection logs = MongoKit.getCollection(collection);
        BasicDBObject conditons = new BasicDBObject();
        buildFilter(filter, conditons);
        buildLike(like, conditons);
        DBCursor dbCursor = logs.find(conditons);
        page(pageNumber, pageSize, dbCursor);
        sort(sort, dbCursor);
        List<Record> records = new ArrayList<Record>();
        while (dbCursor.hasNext()) {
            records.add(toRecord(dbCursor.next()));
        }
        int totalRow = dbCursor.count();
        if (totalRow <= 0) {
            return new Page<Record>(new ArrayList<Record>(0), pageNumber, pageSize, 0, 0);
        }
        int totalPage = totalRow / pageSize;
        if (totalRow % pageSize != 0) {
            totalPage++;
        }
        Page<Record> page = new Page<Record>(records, pageNumber, pageSize, totalPage, totalRow);
        return page;
    }
    
    public static Page<Record> paginate(String collection, int pageNumber, int pageSize, Map<String, Object> filter,
            Map<String, Object> like, BoSorter sort) {
        DBCollection logs = MongoKit.getCollection(collection);
        BasicDBObject conditons = new BasicDBObject();
        buildFilter(filter, conditons);
        buildLike(like, conditons);
        DBCursor dbCursor = logs.find(conditons);
        page(pageNumber, pageSize, dbCursor);
        sort(sort, dbCursor);
        List<Record> records = new ArrayList<Record>();
        while (dbCursor.hasNext()) {
            records.add(toRecord(dbCursor.next()));
        }
        int totalRow = dbCursor.count();
        if (totalRow <= 0) {
            return new Page<Record>(new ArrayList<Record>(0), pageNumber, pageSize, 0, 0);
        }
        int totalPage = totalRow / pageSize;
        if (totalRow % pageSize != 0) {
            totalPage++;
        }
        Page<Record> page = new Page<Record>(records, pageNumber, pageSize, totalPage, totalRow);
        return page;
    }

    public static Page<Record> find(String collection,int pageNumber, int pageSize, BasicDBObject condition, Map<String, Object> sort) { 
        DBCollection dbColls = MongoKit.getCollection(collection);  
        DBCursor dbCursor = dbColls.find(condition);
        page(pageNumber, pageSize, dbCursor);
        sort(sort, dbCursor);
        List<Record> records = new ArrayList<Record>();
        while (dbCursor.hasNext()) {
            records.add(toRecord(dbCursor.next()));
        }
        int totalRow = dbCursor.count();
        if (totalRow <= 0) {
            return new Page<Record>(new ArrayList<Record>(0), pageNumber, pageSize, 0, 0);
        }
        int totalPage = totalRow / pageSize;
        if (totalRow % pageSize != 0) {
            totalPage++;
        }
        Page<Record> page = new Page<Record>(records, pageNumber, pageSize, totalPage, totalRow);
        return page;
    }
    
    public static Page<Record> find(String collection,int pageNumber, int pageSize, BasicDBObject condition,BoSorter sorter) { 
        DBCollection dbColls = MongoKit.getCollection(collection);  
        DBCursor dbCursor = dbColls.find(condition);
        page(pageNumber, pageSize, dbCursor);
        sort(sorter, dbCursor);
        List<Record> records = new ArrayList<Record>();
        while (dbCursor.hasNext()) {
            records.add(toRecord(dbCursor.next()));
        }
        int totalRow = dbCursor.count();
        if (totalRow <= 0) {
            return new Page<Record>(new ArrayList<Record>(0), pageNumber, pageSize, 0, 0);
        }
        int totalPage = totalRow / pageSize;
        if (totalRow % pageSize != 0) {
            totalPage++;
        }
        Page<Record> page = new Page<Record>(records, pageNumber, pageSize, totalPage, totalRow);
        return page;
    }
    
    private static void page(int pageNumber, int pageSize, DBCursor dbCursor) {
        dbCursor = dbCursor.skip((pageNumber - 1) * pageSize).limit(pageSize);
    }

    private static void sort(Map<String, Object> sort, DBCursor dbCursor) {
        if (sort != null) {
            DBObject dbo = new BasicDBObject();
            Set<Entry<String, Object>> entrySet = sort.entrySet();
            for (Entry<String, Object> entry : entrySet) {
                String key = entry.getKey();
                Object val = entry.getValue();
                dbo.put(key, "asc".equalsIgnoreCase(val + "") ? 1 : -1);
            }
            dbCursor = dbCursor.sort(dbo);
        }
    }
    private static void sort(BoSorter sorter, DBCursor dbCursor) {
        if (sorter != null) {
            DBObject dbo = new BasicDBObject();
            for (BoEntry s: sorter.getList()) {
                dbo.put(s.key, "asc".equalsIgnoreCase(s.val + "") ? 1 : -1);
            }
            dbCursor = dbCursor.sort(dbo);
        }
    }

    private static void buildLike(Map<String, Object> like, BasicDBObject conditons) {
        if (like != null) {
            Set<Entry<String, Object>> entrySet = like.entrySet();
            for (Entry<String, Object> entry : entrySet) {
                String key = entry.getKey();
                Object val = entry.getValue();
                conditons.put(key, MongoKit.getLikeStr(val));
            }
        }
    }

    private static void buildFilter(Map<String, Object> filter, BasicDBObject conditons) {
        if (filter != null) {
            Set<Entry<String, Object>> entrySet = filter.entrySet();
            for (Entry<String, Object> entry : entrySet) {
                String key = entry.getKey();
                Object val = entry.getValue();
                conditons.put(key, val);
            }

        }
    }

    @SuppressWarnings("unchecked")
    public static Record toRecord(DBObject dbObject) {
    	if(dbObject != null){
    		Record record = new Record();
    		record.setColumns(dbObject.toMap());
    		return record;
    	}
    	return null;
    }

    public static BasicDBObject getLikeStr(Object findStr) {
        Pattern pattern = Pattern.compile("^.*" + findStr + ".*$", Pattern.CASE_INSENSITIVE);
        return new BasicDBObject("$regex", pattern);
    }

    public static DB getDB() {
        return defaultDb;
    }

    public static DB getDB(String dbName) {
        return client.getDB(dbName);
    }

    public static DBCollection getCollection(String name) {
        return defaultDb.getCollection(name);
    }

    public static DBCollection getDBCollection(String dbName, String collectionName) {
        return getDB(dbName).getCollection(collectionName);
    }

    public static MongoClient getClient() {
        return client;
    }

    public static void setMongoClient(MongoClient client) {
        MongoKit.client = client;
    }

    private static BasicDBObject toDBObject(Map<String, Object> map) {
        BasicDBObject dbObject = new BasicDBObject();
        Set<Entry<String, Object>> entrySet = map.entrySet();
        for (Entry<String, Object> entry : entrySet) {
            String key = entry.getKey();
            Object val = entry.getValue();
            dbObject.append(key, val);
        }
        return dbObject;
    }

    private static BasicDBObject toDbObject(Record record) {
        BasicDBObject object = new BasicDBObject();
        for (Entry<String, Object> e : record.getColumns().entrySet()) {
            object.append(e.getKey(), e.getValue());
        }
        return object;
    }
}
